package service

import (
	"errors"
	"github.com/gin-gonic/gin"
	"github.com/zoumo/goset"
	"gorm.io/gorm"
	"guns-go/global"
	"guns-go/model"
	"guns-go/model/request"
	"guns-go/model/response"
	"guns-go/utils"
	"strconv"
	"strings"
)

func QuerySysMenuById(id *int) (error, *model.SysMenu) {
	var sysMenu *model.SysMenu
	err := global.GVA_DB.Where("id = ?", *id).Find(&sysMenu).Error
	if err != nil {
		return err, nil
	}
	return nil, sysMenu
}

func UpdateSysMenuByIds(ids []int, status int8, tx *gorm.DB) error {
	return tx.Model(model.SysMenu{}).Where("id in (?)", ids).Update("status", status).Error
}

func SaveMenu(menu *model.SysMenu, tx *gorm.DB) error {
	return tx.Create(menu).Error
}

func UpdateSysMenu(menu *model.SysMenu, tx *gorm.DB) error {
	return tx.Model(&menu).Updates(menu).Error
}

func GetLoginPermissions(userId int) (err error, p []string) {

	err, roleIds := GetUserRoleIdList(userId)

	if err != nil {
		return err, nil
	}

	if len(roleIds) > 0 {

		err, menuIds := GetRoleMenuIdList(roleIds)

		if err != nil {
			return err, nil
		}

		err, permissions := QueryMenuBatchByIds(menuIds, true)

		if err != nil {
			return err, nil
		}

		return err, permissions
	}
	return err, make([]string, 0)
}

func QueryMenuBatchByIds(ids []int, isPermissions bool) (err error, pSlice []string) {

	var sysMenus []model.SysMenu
	db := global.GVA_DB.Where("id in (?)", ids)

	if isPermissions {
		err = db.Where("type = 2").Where("status = 0").Find(&sysMenus).Error
	} else {
		err = db.Where("status = 0").Find(&sysMenus).Error
	}

	if err != nil {
		return err, nil
	}

	permissions := make([]string, 0)

	if len(sysMenus) > 0 {

		for _, sysMenu := range sysMenus {

			if isPermissions {
				permissions = append(permissions, sysMenu.Permission)
			} else {
				permissions = append(permissions, sysMenu.Application)
			}
		}
		pSet := goset.NewSetFromStrings(permissions)

		pSet.ToStrings()
		//清空切片
		permissions = permissions[:0]

		permissions = append(permissions, pSet.ToStrings()...)
	}

	return err, permissions
}

func GetUserMenuAppCodeList(userId int) (err error, appCodes []string) {

	apps := make([]string, 0)

	err, roleIds := GetUserRoleIdList(userId)

	if err != nil {
		return err, nil
	}

	if len(roleIds) > 0 {

		err, menuIds := GetRoleMenuIdList(roleIds)

		if err != nil {
			return err, nil
		}

		if len(menuIds) > 0 {

			err, permissions := QueryMenuBatchByIds(menuIds, false)

			if err != nil {
				return err, nil
			}

			if len(permissions) > 0 {
				apps = append(apps, permissions...)
			}
		}
	}

	return err, apps
}

func GetLoginMenusAntDesign(userId int, appCode string) (err error, antDesignMenuTreeNode []*response.LoginMenuTreeNode) {

	//如果是超级管理员则展示所有系统权重菜单，不能展示业务权重菜单
	err, sysUser := QueryUserById(userId)
	if err != nil {
		return err, nil
	}
	adminType := sysUser.AdminType

	var sysMenus []*model.SysMenu

	var db *gorm.DB

	if adminType == global.SuperAdmin {

		db = global.GVA_DB.Where("status = ?", 0).Where("application = ?", appCode).
			Where("type not in (?)", 2).Where("weight not in (?)", 2).Order("sort asc")

	} else {
		//非超级管理员则获取自己角色所拥有的菜单集合
		err, roleIds := GetUserRoleIdList(userId)

		if err != nil {
			return err, nil
		}
		if len(roleIds) > 0 {
			err, menuIds := GetRoleMenuIdList(roleIds)

			if err != nil {
				return err, nil
			}

			if len(menuIds) > 0 {
				db = global.GVA_DB.Where("id in (?)", menuIds).Where("application = ?", appCode).
					Where("status = ?", 0).Where("type not in (?)", 2).Order("sort asc")
			} else {
				return err, nil
			}
		} else {
			return err, nil
		}
	}

	err = db.Find(&sysMenus).Error

	err, antDesignMenuTreeNodeSlice := convertSysMenuToLoginMenu(sysMenus)

	return err, antDesignMenuTreeNodeSlice
}

func convertSysMenuToLoginMenu(menus []*model.SysMenu) (err error, antDesignMenuTreeNode []*response.LoginMenuTreeNode) {

	loginMenuTreeNodeSlice := make([]*response.LoginMenuTreeNode, 0)

	if len(menus) > 0 {

		for _, sysMenu := range menus {

			loginMenuTreeNode := new(response.LoginMenuTreeNode)

			loginMenuTreeNode.Component = sysMenu.Component
			loginMenuTreeNode.Id = sysMenu.Id
			loginMenuTreeNode.Name = sysMenu.Code
			loginMenuTreeNode.Path = sysMenu.Router
			loginMenuTreeNode.Pid = *sysMenu.Pid

			mateItem := new(response.Meta)

			mateItem.Icon = sysMenu.Icon
			mateItem.Title = sysMenu.Name
			mateItem.Link = sysMenu.Link

			//是否可见
			var isShow = !(sysMenu.Visible == global.N)
			mateItem.Show = &isShow
			//设置的首页，默认打开此链接
			loginMenuTreeNode.Redirect = sysMenu.Redirect

			var outer int8
			outer = 3
			if sysMenu.OpenType == &outer {
				var targetType = "_blank"
				mateItem.Target = &targetType
				loginMenuTreeNode.Path = sysMenu.Link
				loginMenuTreeNode.Redirect = sysMenu.Link
			}

			loginMenuTreeNode.Meta = mateItem

			loginMenuTreeNodeSlice = append(loginMenuTreeNodeSlice, loginMenuTreeNode)
		}
	}

	return err, loginMenuTreeNodeSlice
}

func HasMenu(appCode string) (error, bool) {

	var sysMenuSlice []*model.SysMenu

	err := global.GVA_DB.Where("application = ?", appCode).Where("status <> ?", 2).Find(&sysMenuSlice).Error

	if err != nil {
		return err, false
	}
	return nil, len(sysMenuSlice) != 0
}

func MenuList(sysMenuParam *request.SysMenuParam) (error, []*model.SysMenu) {

	var sysMenuSlice []*model.SysMenu
	db := global.GVA_DB

	if sysMenuParam != nil {

		//根据所属应用查询
		if sysMenuParam.Application != "" {
			db = db.Where("application = ?", sysMenuParam.Application)
		}

		//根据菜单名称模糊查询
		if sysMenuParam.Name != nil {
			db = db.Where("name = ?", "%"+*sysMenuParam.Name+"%")
		}
	}

	db = db.Where("status = ?", 0)

	//根据排序升序排列，序号越小越在前
	db = db.Order("sort asc")

	err := db.Find(&sysMenuSlice).Error

	if err != nil {
		return err, nil
	}

	return nil, doTreeBuildMenu(sysMenuSlice)
}

func doTreeBuildMenu(sysMenuSlice []*model.SysMenu) (menuS []*model.SysMenu) {

	//具体构建的过程
	buildComplete := executeBuildingMenu(sysMenuSlice)

	//构建之后的处理工作
	return afterBuildMenu(buildComplete)
}

func executeBuildingMenu(sysMenuSlice []*model.SysMenu) (dictNodeS []*model.SysMenu) {

	for _, node := range sysMenuSlice {
		buildChildNodesMenu(sysMenuSlice, node, make([]*model.SysMenu, 0))
	}

	return sysMenuSlice
}

func buildChildNodesMenu(totalNodes []*model.SysMenu, node *model.SysMenu, childNodeSlice []*model.SysMenu) {

	if len(totalNodes) <= 0 || node == nil {
		return
	}

	nodeSubSlice := getSubChildLevelOneMenu(totalNodes, node)

	if len(nodeSubSlice) > 0 {
		for _, t := range nodeSubSlice {
			buildChildNodesMenu(totalNodes, t, make([]*model.SysMenu, 0))
		}
	}

	childNodeSlice = append(childNodeSlice, nodeSubSlice...)
	node.Children = childNodeSlice
}

func getSubChildLevelOneMenu(slice []*model.SysMenu, node *model.SysMenu) (nodeS []*model.SysMenu) {

	nodeSlice := make([]*model.SysMenu, 0)

	if len(slice) > 0 {
		for _, t := range slice {
			if *t.Pid == node.Id {
				nodeSlice = append(nodeSlice, t)
			}
		}
	}
	return nodeSlice
}

func afterBuildMenu(nodes []*model.SysMenu) (results []*model.SysMenu) {

	//去掉所有的二级节点
	resultSlice := make([]*model.SysMenu, 0)

	for _, node := range nodes {
		if *node.Pid == 0 {
			resultSlice = append(resultSlice, node)
		}
	}

	return resultSlice
}

func MenuTree(sysMenuParam *request.SysMenuParam) (error, []*response.AntdBaseTreeNode) {

	menuTreeNodeSlice := make([]*response.AntdBaseTreeNode, 0)

	var sysMenuSlice []*model.SysMenu
	db := global.GVA_DB

	if sysMenuParam != nil {

		if sysMenuParam.Application != "" {
			db = db.Where("application = ?", sysMenuParam.Application)
		}
	}

	MenuTypes := make([]int, 0)
	MenuTypes = append(MenuTypes, 0)
	MenuTypes = append(MenuTypes, 1)

	db = db.Where("status = ? and type in(?)", 0, MenuTypes)
	//根据排序升序排列，序号越小越在前
	db = db.Order("sort asc")

	err := db.Find(&sysMenuSlice).Error

	if err != nil {
		return err, nil
	}

	if len(sysMenuSlice) > 0 {

		for _, sysMenu := range sysMenuSlice {
			menuTreeNode := new(response.AntdBaseTreeNode)

			menuTreeNode.Id = sysMenu.Id
			menuTreeNode.Pid = *sysMenu.Pid
			menuTreeNode.ParentId = *sysMenu.Pid
			menuTreeNode.Title = *sysMenu.Name
			menuTreeNode.Value = strconv.Itoa(sysMenu.Id)
			menuTreeNode.Weight = sysMenu.Sort

			menuTreeNodeSlice = append(menuTreeNodeSlice, menuTreeNode)
		}
	}

	return err, doTreeBuildOrg(menuTreeNodeSlice)
}

func MenuAdd(sysMenuParam *request.SysMenuParam) error {

	// 校验参数
	err := checkParamMenu(sysMenuParam, false)

	if err != nil {
		return err
	}

	sysMenu := new(model.SysMenu)

	err = utils.SimpleCopyProperties(sysMenu, sysMenuParam)

	if err != nil {
		return err
	}

	// 设置新的pid
	pids := createNewPids(sysMenuParam.Pid)

	sysMenu.Pids = pids

	// 设置启用状态
	var zero int8
	zero = 0
	sysMenu.Status = &zero

	tx := global.GVA_DB.Begin()

	err = SaveMenu(sysMenu, tx)

	if err != nil {
		tx.Rollback()
		return err
	}
	tx.Commit()
	return nil
}

func createNewPids(pid *int) string {

	if pid != nil && *pid == 0 {
		return global.LeftSquareBrackets + strconv.Itoa(0) + global.RightSquareBrackets + global.COMMA
	} else {

		//获取父菜单
		err, parentMenu := QuerySysMenuById(pid)

		if err != nil {
			return ""
		}
		return parentMenu.Pids + global.LeftSquareBrackets + strconv.Itoa(*pid) + global.RightSquareBrackets + global.COMMA
	}
}

func checkParamMenu(sysMenuParam *request.SysMenuParam, isExcludeSelf bool) error {

	//菜单类型（字典 0目录 1菜单 2按钮）
	menuType := sysMenuParam.Type
	router := sysMenuParam.Router
	permission := sysMenuParam.Permission
	openType := sysMenuParam.OpenType

	if *menuType == 0 {
		if router == nil || *router == "" {
			return errors.New("路由地址为空，请检查router参数")
		}
	}

	if *menuType == 1 {
		if router == nil || *router == "" {
			return errors.New("路由地址为空，请检查router参数")
		}

		if openType == nil {
			return errors.New("打开方式为空，请检查openType参数")
		}
	}

	if *menuType == 2 {
		if permission == "" {
			return errors.New("权限标识为空，请检查permission参数")
		} else {

			urlSlice := global.GVA_ROUTER

			isContain := strings.Contains(permission, global.COLON)

			if !isContain {
				return errors.New("权限标识格式错误，请检查permission参数")
			}

			permission = ":" + permission
			url := strings.ReplaceAll(permission, global.COLON, global.LeftDivide)
			uslContain, err := utils.Contain(url, urlSlice)

			if err != nil {
				return err
			}
			if !uslContain {
				return errors.New("权限不存在，请检查permission参数")
			}
		}
	}

	// 如果是编辑菜单时候，pid和id不能一致，一致会导致无限递归
	if isExcludeSelf {
		if sysMenuParam.Id == sysMenuParam.Pid {
			return errors.New("父级菜单不能为当前节点，请从新选择父级菜单")
		}
	}

	id := sysMenuParam.Id
	name := sysMenuParam.Name
	code := sysMenuParam.Code

	var sysMenuSlice []*model.SysMenu

	db := global.GVA_DB
	// 查询名称有无重复
	dbName := db.Where("name = ?", name).Where("status <> ?", 2)
	// 查询编码有无重复
	dbCode := db.Where("code = ?", code).Where("status <> ?", 2)

	if isExcludeSelf {
		dbName = dbName.Where("id <> ?", id)
		dbCode = dbCode.Where("id <> ?", id)
	}

	var countByName int64
	err := dbName.Find(&sysMenuSlice).Count(&countByName).Error

	if err != nil {
		return err
	}

	var countByCode int64
	err = dbCode.Find(&sysMenuSlice).Count(&countByCode).Error

	if err != nil {
		return err
	}

	if countByName >= 1 {
		return errors.New("菜单名称重复，请检查name参数")
	}

	if countByCode >= 1 {
		return errors.New("菜单编码重复，请检查code参数")
	}
	return nil
}

func MenuEdit(sysMenuParam *request.SysMenuParam) error {

	// 校验参数
	err := checkParamMenu(sysMenuParam, true)

	if err != nil {
		return err
	}

	// 获取修改的菜单的旧数据（库中的）
	err, oldSysMenu := QuerySysMenuById(sysMenuParam.Id)

	if err != nil {
		return err
	}

	if oldSysMenu.Id == 0 {
		return errors.New("菜单不存在")
	}

	// 本菜单旧的pids
	oldPid := oldSysMenu.Pid
	oldPids := oldSysMenu.Pids

	// 生成新的pid和pids
	newPid := sysMenuParam.Pid
	newPids := createNewPids(sysMenuParam.Pid)

	// 是否更新子应用的标识
	updateSubAppsFlag := false

	// 是否更新子节点的pids的标识
	updateSubPidsFlag := false

	// 如果应用有变化
	if sysMenuParam.Application != oldSysMenu.Application {
		// 父节点不是根节点不能移动应用
		if *oldPid != 0 {
			return errors.New("父节点不是根节点不能移动应用")
		}
		updateSubAppsFlag = true
	}

	// 父节点有变化
	if newPid != oldPid {
		updateSubPidsFlag = true
	}

	tx := global.GVA_DB.Begin()

	// 开始更新所有子节点的配置
	if updateSubAppsFlag || updateSubPidsFlag {

		// 查找所有叶子节点，包含子节点的子节点
		var sysMenuSlice []model.SysMenu
		err = global.GVA_DB.Where("pids like ?", "%"+strconv.Itoa(oldSysMenu.Id)+"%").Find(&sysMenuSlice).Error

		if err != nil {
			return err
		}

		if len(sysMenuSlice) > 0 {
			// 更新所有子节点的application
			for _, sysMenu := range sysMenuSlice {
				sysMenu.Application = sysMenuParam.Application
			}

			// 更新所有子节点的pids
			if updateSubPidsFlag {

				for _, child := range sysMenuSlice {
					// 子节点pids组成 = 当前菜单新pids + 当前菜单id + 子节点自己的pids后缀
					oldParentCodesPrefix := oldPids + global.LeftSquareBrackets + strconv.Itoa(oldSysMenu.Id) + global.RightSquareBrackets + global.COMMA
					oldParentCodesSuffix := child.Pids[len(oldParentCodesPrefix):]
					menuParentCodes := newPids + global.LeftSquareBrackets + strconv.Itoa(oldSysMenu.Id) + global.RightSquareBrackets + global.COMMA + oldParentCodesSuffix

					child.Pids = menuParentCodes
				}
			}

			for _, newSysMenu := range sysMenuSlice {
				err = UpdateSysMenu(&newSysMenu, tx)
				if err != nil {
					tx.Rollback()
					return err
				}
			}
		}
	}

	// 拷贝参数到实体中
	err = utils.SimpleCopyProperties(oldSysMenu, sysMenuParam)

	if err != nil {
		tx.Rollback()
		return err
	}

	// 设置新的pids
	oldSysMenu.Pids = newPids

	//不能修改状态，用修改状态接口修改状态
	oldSysMenu.Status = nil

	err = UpdateSysMenu(oldSysMenu, tx)

	if err != nil {
		tx.Rollback()
		return err
	}
	tx.Commit()
	return nil
}

func MenuDelete(sysMenuParam *request.SysMenuParam) error {

	id := sysMenuParam.Id
	//级联删除子节点
	err, childIdSlice := getChildIdListById(*id)
	if err != nil {
		return err
	}

	childIdSlice = append(childIdSlice, *id)

	tx := global.GVA_DB.Begin()

	err = UpdateSysMenuByIds(childIdSlice, 2, tx)

	if err != nil {
		tx.Rollback()
		return err
	}

	//级联删除该菜单及子菜单对应的角色-菜单表信息
	err = deleteRoleMenuListByMenuIdList(childIdSlice, tx)

	if err != nil {
		tx.Rollback()
		return err
	}
	tx.Commit()
	return nil
}

func getChildIdListById(id int) (error, []int) {

	childIdSlice := make([]int, 0)
	var sysMenuSlice []*model.SysMenu
	db := global.GVA_DB.Where("pids like ?", global.LeftSquareBrackets+strconv.Itoa(id)+global.RightSquareBrackets)

	err := db.Find(&sysMenuSlice).Error

	if err != nil {
		return err, nil
	}

	if len(sysMenuSlice) > 0 {

		for _, sysMenu := range sysMenuSlice {
			childIdSlice = append(childIdSlice, sysMenu.Id)
		}
	}

	return nil, childIdSlice
}

func TreeForGrant(userId int, sysMenuParam *request.SysMenuParam, c *gin.Context) (error, []*response.AntdBaseTreeNode) {

	menuTreeNodeSlice := make([]*response.AntdBaseTreeNode, 0)

	var sysMenuSlice []*model.SysMenu
	db := global.GVA_DB

	if sysMenuParam != nil {

		//根据所属应用查询
		if sysMenuParam.Application != "" {
			db = db.Where("application = ?", sysMenuParam.Application)
		}
	}

	//如果是超级管理员给角色授权菜单时可选择所有菜单
	err, superAdmin := IsSuperAdmin(c)

	if err != nil {
		return err, nil
	}

	if superAdmin {
		db = db.Where("status", 0)
	} else {

		//非超级管理员则获取自己拥有的菜单，分配给人员，防止越级授权
		err, roleIdSlice := GetUserRoleIdList(userId)

		if err != nil {
			return err, nil
		}

		if len(roleIdSlice) > 0 {
			err, menuIds := GetRoleMenuIdList(roleIdSlice)

			if err != nil {
				return err, nil
			}
			if len(menuIds) > 0 {
				db = db.Where("id in (?)", menuIds).Where("status = ?", 0)
			} else {
				//如果角色的菜单为空，则查不到菜单
				return nil, menuTreeNodeSlice
			}
		} else {
			//如果角色为空，则根本没菜单
			return nil, menuTreeNodeSlice
		}
	}

	//根据排序升序排列，序号越小越在前
	err = db.Order("sort asc").Find(&sysMenuSlice).Error

	if err != nil {
		return err, nil
	}

	if len(sysMenuSlice) > 0 {

		for _, sysMenu := range sysMenuSlice {
			menuTreeNode := new(response.AntdBaseTreeNode)

			menuTreeNode.Id = sysMenu.Id
			menuTreeNode.Pid = *sysMenu.Pid
			menuTreeNode.ParentId = *sysMenu.Pid
			menuTreeNode.Title = *sysMenu.Name
			menuTreeNode.Value = strconv.Itoa(sysMenu.Id)
			menuTreeNode.Weight = sysMenu.Sort

			menuTreeNodeSlice = append(menuTreeNodeSlice, menuTreeNode)
		}
	}

	return err, doTreeBuildOrg(menuTreeNodeSlice)
}
